﻿using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Roslyn.Compilers.Common;
using Roslyn.Compilers.CSharp;
using RoslynHelper;

namespace ParameterizedNewGenericConstraint
{
    internal class GenericClassInstanciationRewriter : SyntaxRewriter
    {
        private ISemanticModel _semanticModel;
        private Dictionary<ClassDeclarationSyntax, Dictionary<string, List<TypeConstraintSyntax>>> _typesWithFactory = new Dictionary<ClassDeclarationSyntax, Dictionary<string, List<TypeConstraintSyntax>>>();

        public GenericClassInstanciationRewriter(ISemanticModel semanticModel, Dictionary<ClassDeclarationSyntax, Dictionary<string, List<TypeConstraintSyntax>>> typesWithFactory)
        {
            _semanticModel = semanticModel;
            _typesWithFactory = typesWithFactory;
        }

        protected override SyntaxNode VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
        {
            var nodeTypeSymbol = _semanticModel.GetSemanticInfo(node.Type).Symbol;
            if (nodeTypeSymbol == null)
                return base.VisitObjectCreationExpression(node);
            var originalDefinitionCtor = nodeTypeSymbol.OriginalDefinition;
            var typeSymbol = originalDefinitionCtor as ITypeSymbol;
            var typeWithFactory = _typesWithFactory.FirstOrDefault(twf => twf.Key.Identifier.GetText() == (typeSymbol == null ? originalDefinitionCtor.ContainingType.Name : typeSymbol.Name) && ((NamespaceDeclarationSyntax)twf.Key.Parent).Name.GetText() == originalDefinitionCtor.ContainingNamespace.Name);
            if (typeWithFactory.Key == null)
                return node;
            string typeName = Regex.Match(nodeTypeSymbol.ToString(), @"^[^<]+<((.?)+)>[^>]+$").Groups[1].Value;
            return node.AddInitializers(
                from tcc in typeWithFactory.Value
                select Syntax.BinaryExpression(
                    kind: SyntaxKind.AssignExpression, 
                    left: Syntax.IdentifierName("Factory" + tcc.Key),
                    operatorToken: Syntax.Token(SyntaxKind.EqualsToken),
                    right: Syntax.ParenthesizedLambdaExpression(
                        parameterList: Syntax.ParameterList(
                            parameters: SeparatedList.Create(
                                tcc.Value.Select((tc, tcIndex) => 
                                    Syntax.Parameter(
                                        identifier: Syntax.Identifier(string.Concat("a", tcc.Key, tcIndex.ToString())))))), 
                        body: Syntax.ObjectCreationExpression(
                            type: Syntax.ParseTypeName(typeName),
                            argumentListOpt: Syntax.ArgumentList(
                                arguments: SeparatedList.Create(
                                    tcc.Value.Select((tc, tcIndex) => Syntax.Argument(
                                        expression: Syntax.IdentifierName(string.Concat("a", tcc.Key, tcIndex.ToString()))))))))));
        }
    }
}
